#pragma once
#include "opcode_base.hpp"

namespace rvfft {

/* complex_mul*(layer=k) (start from 0)
 * v1 [0]    [1]    [2]    [3]
 * >>>
 * v1 [0]    [1]*Wk [2]    [3]*Wk   complex_mul0
 * v1 [0]    [1]    [2]*Wk [3]*Wk   complex_mul1
 * v1 [0]*Wk [1]*Wk [2]*Wk [3]*Wk   complex_mul2
 */
template<uint8_t type>
RET_OPCODE complex_mul(uint8_t layer, uint8_t i, uint8_t reg_vec)
{
  Opcode<3> funct3;
  if constexpr (type==0)      { funct3 = 0b100; }
  else if constexpr (type==1) { funct3 = 0b101; }
  else if constexpr (type==2) { funct3 = 0b110; }
  else static_assert(always_false<bool>);

  return InstrBase::complex(layer, i, reg_vec, funct3, reg_vec);
}

/* shuffle_single*
 * v1[0][1][2][3]
 * >>>
 * v1[0][2][1][3]
 */
RET_OPCODE shuffle_single(auto imm_4, auto rs2_5, auto rs1_5, auto rd_5)
{
  return InstrBase::shuffle(imm_4, 0b000, rs2_5, rs1_5, rd_5);
}

/* shuffle_double*
 * v1[0][1][2][3]  v2[4][5][6][7]
 * >>>
 * v1[0][4][2][6]  v2[1][5][3][7]   shuffle_double0
 * v1[0][1][4][5]  v2[2][3][6][7]   shuffle_double1
 */
RET_OPCODE shuffle_double0(auto imm_4, auto rs2_5, auto rs1_5, auto rd_5)
{
  return InstrBase::shuffle(imm_4, 0b001, rs2_5, rs1_5, rd_5);
}

RET_OPCODE shuffle_double1(auto imm_4, auto rs2_5, auto rs1_5, auto rd_5)
{
  return InstrBase::shuffle(imm_4, 0b010, rs2_5, rs1_5, rd_5);
}

/* butterfly_single*
 * v1 [0]       [1]       [2]       [3]
 * >>>
 * v1 ([0]+[1]) ([0]-[1]) ([2]+[3]) ([2]-[3])  butterfly_single0
 * v1 ([0]+[2]) ([1]+[3]) ([0]-[2]) ([1]-[3])  butterfly_single1
 */
template<uint8_t type>
RET_OPCODE butterfly_single(uint8_t reg_vec)
{
  Opcode<3> imm_3;
  if constexpr (type==0)      { imm_3 = 0b000; }
  else if constexpr (type==1) { imm_3 = 0b001; }
  else static_assert(always_false<bool>);

  return InstrBase::butterfly(0, imm_3, 0, reg_vec, reg_vec);
}

/* butterfly_double
 * v1 [0][1][2][3] 
 * v2 [4][5][6][7]
 * >>>
 * v1 ([0]+[4]) ([1]+[5]) ([2]+[6]) ([3]-[7])
 * v2 ([0]-[4]) ([1]-[5]) ([2]-[6]) ([3]-[7])
 */
RET_OPCODE butterfly_double(uint8_t reg_vec0, uint8_t reg_vec1)
{
  return InstrBase::butterfly(0, 0b011, reg_vec1, reg_vec0, reg_vec0);
}

//配合buffterfly0 shuffle0使用，将另一半128bits存入rvec中
RET_OPCODE write_buffer(uint8_t reg_vec)
{
  Opcode<5> rd_5  = reg_vec;
  return InstrBase::butterfly(0, 0b111, 0, 0, rd_5);
}

/**
 * x[reg_base_addr]+offset -> x[reg_vec]
 */
template<uint8_t type>
RET_OPCODE LDVEC(uint8_t reg_base_addr, int offset, uint8_t reg_vec)
{
  Opcode<3> funct3;
  if constexpr (type==0)      { funct3 = 0b110; }
  else if constexpr (type==1) { funct3 = 0b111; }
  else static_assert(always_false<bool>);

  return InstrBase::ld_vec(offset, reg_base_addr, funct3, reg_vec);
}

static inline std::pair<Opcode<7>, Opcode<5>> split_offset(Opcode<12> offset)
{
  return std::make_pair(
    Opcode<7>{offset.to_string().substr(0,7)}, 
    Opcode<5>{offset.to_string().substr(7,5)}
    );
}

/**
 * x[reg_vec] -> x[reg_base_addr]+offset
 */
template<uint8_t type>
RET_OPCODE SDVEC(uint8_t reg_vec, uint8_t reg_base_addr, int offset)
{
  auto [offset11_5, offset4_0] = rvfft::split_offset(offset);

  Opcode<3> funct3;
  if constexpr (type==0)      { funct3 = 0b100; }
  else if constexpr (type==1) { funct3 = 0b101; }
  else static_assert(always_false<bool>);

  return InstrBase::st_vec(offset11_5, reg_vec, reg_base_addr, funct3, offset4_0);
}

template<uint8_t type>
RET_OPCODE test_write(uint16_t imm, uint8_t reg)
{
  Opcode<4> funct_4;
  if constexpr (type==1)      { funct_4 = 0b0001; }
  else if constexpr (type==2) { funct_4 = 0b1001; }
  else if constexpr (type==3) { funct_4 = 0b0010; }
  else if constexpr (type==4) { funct_4 = 0b1010; }
  else if constexpr (type==5) { funct_4 = 0b0011; }
  else if constexpr (type==6) { funct_4 = 0b1011; }
  else if constexpr (type==7) { funct_4 = 0b0100; }
  else if constexpr (type==8) { funct_4 = 0b1100; }
  else static_assert(always_false<bool>);

  return InstrBase::test_w(imm, funct_4, reg);
}
}
